/**
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import huks from '@ohos.security.huks';
import util from '@ohos.util';
import LogUtil from '../../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
import BaseModel from '../../../../../../../common/utils/src/main/ets/default/model/BaseModel';
import VpnConfig from './VpnConfig';


const MODULE_TAG: string = 'setting_vpn:VPNHuksModel:';
const HUKS_VPN_ALIAS = 'vpn_aesKeyAlias'
const CHALLENG_LEN = 6;
const ALWAYSVAILD = 4;
const ENCODEINTO_BUFFER = 20;
const DECRYPT_BYTE = 16;
const IV: string = (Math.floor(Math.random() * 1000000000000) + 1).toString();
const NONCE = '123456789012';
const AAD = '124567890123456';
const AEAD = '124567890123456';

let challengeNew = new Uint8Array(CHALLENG_LEN);

// AES加密密钥属性信息
function getAesEncryptProperties(properties): void {
  let index = 0;
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_PADDING,
    value: huks.HuksKeyPadding.HUKS_PADDING_NONE
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
    value: huks.HuksCipherMode.HUKS_MODE_GCM
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA,
    value: stringToUint8Array(AAD)
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_NONCE,
    value: stringToUint8Array(NONCE)
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_AE_TAG,
    value: stringToUint8Array(AEAD)
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_IV,
    value: stringToUint8Array(IV)
  };
  return;
}

// AES解密密钥属性信息
function getAesDecryptProperties(properties, info): void {
  let index = 0;
  let t = info;
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_PADDING,
    value: huks.HuksKeyPadding.HUKS_PADDING_NONE
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
    value: huks.HuksCipherMode.HUKS_MODE_GCM
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_ASSOCIATED_DATA,
    value: stringToUint8Array(AAD)
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_NONCE,
    value: stringToUint8Array(NONCE)
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_AE_TAG,
    value: t.slice(t.length - DECRYPT_BYTE)
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_IV,
    value: stringToUint8Array(IV)
  };
  return;
}

// 生成AES密钥属性信息
function getAesGenerateProperties(properties): void {
  let index = 0;
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_PURPOSE,
    value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT |
    huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_PADDING,
    value: huks.HuksKeyPadding.HUKS_PADDING_NONE
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE,
    value: huks.HuksCipherMode.HUKS_MODE_GCM
  };
  return;
}

function getAesPublicProperties(properties) {
  let index = 0;
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_ALGORITHM,
    value: huks.HuksKeyAlg.HUKS_ALG_AES
  };
  properties[index++] = {
    tag: huks.HuksTag.HUKS_TAG_KEY_SIZE,
    value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128
  };
}

function stringToUint8Array(str: string): Uint8Array {
  let arr = [];
  for (let i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
  }
  return new Uint8Array(arr);
}

function uint8ArrayToString(fileData): string {
  let dataString = '';
  for (let i = 0; i < fileData.length; i++) {
    dataString += String.fromCharCode(fileData[i]);
  }
  return dataString;
}

function base64ToArrayBuffer(info): Uint8Array {
  return new util.Base64().decodeSync(info);
}

function base64ToString(byte): string {
  return new util.Base64().encodeToStringSync(byte);
}

function encodeInto(str: string): Uint8Array {
  let textEncoder = new util.TextEncoder();
  let buffer = new ArrayBuffer(ENCODEINTO_BUFFER);
  let result = new Uint8Array(buffer);
  result = textEncoder.encodeInto(str);

  console.info('retStr==' + result);
  return result;
}

/**
 * VPNHuksModel service class
 */
export class HuksModel extends BaseModel {
  isKeyItemExist:boolean = false;

  constructor() {
    super();
    LogUtil.info(MODULE_TAG + `constructor`);
    this.init()
  }

  private async init():Promise<void>{
    LogUtil.info(MODULE_TAG + `isKeyItemExist init`);
    huks.isKeyItemExist(HUKS_VPN_ALIAS,{properties:[]},(error, result) =>{
      LogUtil.info(MODULE_TAG + `isKeyItemExist error = ${error} exsit=${result}`);
      if(result) {
         this.isKeyItemExist = true;
      } else {
        this.isKeyItemExist = false;
        this.generateKeyItem();
      }
    })

  }

  /**
   * 生成vpn密钥
   */
  private async generateKeyItem():Promise<void>{
    let generateKeyProperties = new Array();
    let publicProperties = new Array();
    getAesGenerateProperties(generateKeyProperties);
    getAesPublicProperties(publicProperties);
    let generateKeyOptions = {
      properties: publicProperties.concat(generateKeyProperties)
    };
    await huks.generateKeyItem(HUKS_VPN_ALIAS, generateKeyOptions).then((data) => {
    }).catch((err) => {
      LogUtil.error(MODULE_TAG + `generate key failed, ${JSON.stringify(err.code)}: ${JSON.stringify(err.message)}`);
    });
  }

  /**
   * 使用HUKS生成的新密钥加密vpn
   * @param plainText
   * @param resultCallback
   * @returns
   */
  async encryptData(vpnConfig:VpnConfig, resultCallback: (profile:Uint8Array) => void): Promise<void> {
    LogUtil.info(MODULE_TAG + `encryptData start`);
    let plainText:string = JSON.stringify(vpnConfig);
    let handle;
    let publicProperties = new Array();
    getAesPublicProperties(publicProperties);
    let encryptProperties = new Array();
    getAesEncryptProperties(encryptProperties);
    let encryptOptions = {
      properties: publicProperties.concat(encryptProperties),
      inData: new Uint8Array(new Array())
    };
    await huks.initSession(HUKS_VPN_ALIAS, encryptOptions).then((data) => {
      handle = data.handle;
    }).catch((err) => {
      LogUtil.error(MODULE_TAG + `encrypt initSession failed, ${JSON.stringify(err.code)}: ${JSON.stringify(err.message)}`);
    });
    encryptOptions.inData = encodeInto(plainText);
    await huks.finishSession(handle, encryptOptions).then((data) => {
      let cipherData: Uint8Array = data.outData;
      resultCallback(cipherData);
    }).catch((err) => {
      LogUtil.error(MODULE_TAG + `encrypt finishSession failed, ${JSON.stringify(err.code)}: ${JSON.stringify(err.message)}`);
    });
  }

  /**
   * 使用HUKS生成的新密钥进行解密
   * @param cipherDataArray
   * @param resultCallback
   * @returns
   */
  async decryptData(cipherDataArray: Uint8Array): Promise<VpnConfig> {
    LogUtil.info(MODULE_TAG + `decrypt start`);
    let decryptOptions = new Array();
    let publicProperties = new Array();
    getAesDecryptProperties(decryptOptions, cipherDataArray);
    getAesPublicProperties(publicProperties);
    let options = {
      properties: publicProperties.concat(decryptOptions),
      inData: cipherDataArray
    };

    try {
      let sessionHandle = await huks.initSession(HUKS_VPN_ALIAS, options);
      let handler = sessionHandle.handle;
      options.inData = cipherDataArray.slice(0, cipherDataArray.length - DECRYPT_BYTE);
      let data = await huks.finishSession(handler, options);
      let str: string = uint8ArrayToString(data.outData);
      return JSON.parse(str) as VpnConfig;
    } catch (error) {
      LogUtil.error(MODULE_TAG + `decrypt finishSession failed, ${JSON.stringify(error)}}`);
      return null;
    }
  }
}
let huksModel = new HuksModel();
export default huksModel;

